"""统一云盘客户端

通过虚拟目录树的方式统一管理多个云盘provider，屏蔽各个接入方的差异。
"""

from typing import Dict, List, Optional, Any, Union, Tuple
import os
import logging
from pathlib import Path

from .base import BaseCloudProvider, CloudFile, UploadResult, DownloadResult, FileType
from .factory import CloudProviderFactory
from .directory_manager import DirectoryManager
from .provider_manager import ProviderManager


class DirectoryNode:
    """目录节点，用于构建虚拟目录树"""
    
    def __init__(self, name: str, path: str, provider_type: str = "", 
                 provider_path: str = "", is_virtual: bool = False):
        self.name = name
        self.path = path  # 统一路径
        self.provider_type = provider_type  # 对应的provider类型
        self.provider_path = provider_path  # provider内部路径
        self.is_virtual = is_virtual  # 是否为虚拟目录
        self.children: Dict[str, 'DirectoryNode'] = {}
        self.parent: Optional['DirectoryNode'] = None
        self.metadata: Dict[str, Any] = {}
    
    def add_child(self, child: 'DirectoryNode'):
        """添加子节点"""
        child.parent = self
        self.children[child.name] = child
    
    def get_child(self, name: str) -> Optional['DirectoryNode']:
        """获取子节点"""
        return self.children.get(name)
    
    def get_full_path(self) -> str:
        """获取完整路径"""
        if self.parent is None:
            return "/"
        parent_path = self.parent.get_full_path()
        if parent_path == "/":
            return f"/{self.name}"
        return f"{parent_path}/{self.name}"
    
    def is_provider_root(self) -> bool:
        """是否为provider根目录"""
        return bool(self.provider_type and not self.is_virtual)


class UnifiedCloudClient:
    """统一云盘客户端
    
    通过虚拟目录树管理多个云盘provider，提供统一的文件操作接口。
    各个SDK内部自行实现管理逻辑，对外提供一致的目录结构。
    """
    
    def __init__(self, max_workers: int = 5):
        """
        初始化统一客户端
        
        Args:
            max_workers: 最大工作线程数
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        
        # 初始化管理器
        self.directory_manager = DirectoryManager()
        self.provider_manager = ProviderManager(max_workers=max_workers)
        
        # 初始化路径缓存
        self._path_cache = {}
        
        # 初始化根节点
        self.root = DirectoryNode("/", "/", is_virtual=True)
        
        self.logger.info("统一云盘客户端初始化完成")
    
    def mount_provider(self, mount_point: str, provider_type: str, 
                      config: Dict[str, Any], auto_login: bool = True) -> bool:
        """
        挂载云盘provider到指定路径
        
        Args:
            mount_point: 挂载点路径，如 "/tianyi", "/baidu"
            provider_type: provider类型
            config: provider配置
            auto_login: 是否自动登录
            
        Returns:
            bool: 挂载是否成功
        """
        try:
            # 创建目录挂载点
            if not self.directory_manager.create_mount_point(mount_point, provider_type, config):
                self.logger.error(f"创建目录挂载点失败: {mount_point}")
                return False
            
            # 创建provider实例
            if not self.provider_manager.create_provider(mount_point, provider_type, config, auto_login):
                self.logger.error(f"创建provider失败: {provider_type}")
                # 清理目录挂载点
                self.directory_manager.remove_mount_point(mount_point)
                return False
            
            self.logger.info(f"成功挂载 {provider_type} 到 {mount_point}")
            return True
            
        except Exception as e:
            self.logger.error(f"挂载provider失败: {e}")
            return False
    
    def _create_mount_point(self, mount_point: str, provider_type: str) -> Optional[DirectoryNode]:
        """创建挂载点"""
        # 规范化路径
        mount_point = mount_point.strip("/")
        if not mount_point:
            self.logger.error("挂载点不能为根目录")
            return None
        
        # 分解路径
        parts = mount_point.split("/")
        current_node = self.root
        current_path = ""
        
        # 逐级创建目录节点
        for part in parts:
            current_path = f"{current_path}/{part}" if current_path else f"/{part}"
            
            if part not in current_node.children:
                # 判断是否为最后一级（provider根目录）
                is_provider_root = (part == parts[-1])
                node = DirectoryNode(
                    name=part,
                    path=current_path,
                    provider_type=provider_type if is_provider_root else "",
                    provider_path="/" if is_provider_root else "",
                    is_virtual=not is_provider_root
                )
                current_node.add_child(node)
                self._path_cache[current_path] = node
            
            current_node = current_node.children[part]
        
        return current_node
    
    def list_directory(self, path: str = "/") -> List[CloudFile]:
        """
        列出目录内容
        
        Args:
            path: 目录路径
            
        Returns:
            List[CloudFile]: 文件列表
        """
        try:
            # 获取目录节点
            node = self._get_node_by_path(path)
            if not node:
                self.logger.error(f"路径不存在: {path}")
                return []
            
            files = []
            
            # 如果是虚拟目录，返回子节点
            if node.is_virtual:
                for child_name, child_node in node.children.items():
                    file_type = "folder"
                    files.append(CloudFile(
                        file_id=child_node.path,
                        name=child_name,
                        size=0,
                        file_type=file_type,
                        path=child_node.path
                    ))
            
            # 如果是provider目录，调用provider的list方法
            elif node.provider_type:
                provider = self.provider_manager.get_provider(self._get_mount_point(path))
                if provider:
                    provider_files = provider.list(node.provider_path)
                    # 转换路径为统一路径
                    for file in provider_files:
                        unified_path = self._convert_to_unified_path(path, file.name)
                        file.path = unified_path
                        files.append(file)
            
            return files
            
        except Exception as e:
            self.logger.error(f"列出目录失败: {e}")
            return []
    
    def upload_file(self, local_path: str, remote_path: str, 
                   rapid_upload: bool = True) -> UploadResult:
        """
        上传文件
        
        Args:
            local_path: 本地文件路径
            remote_path: 远程文件路径
            rapid_upload: 是否启用秒传
            
        Returns:
            UploadResult: 上传结果
        """
        try:
            # 获取目标目录的provider
            provider, provider_path = self._resolve_provider_path(remote_path)
            if not provider:
                return UploadResult(False, message="无法解析目标路径对应的provider")
            
            # 调用provider的上传方法
            return provider.upload(local_path, provider_path, rapid_upload)
            
        except Exception as e:
            self.logger.error(f"上传文件失败: {e}")
            return UploadResult(False, message=str(e))
    
    def download_file(self, remote_path: str, local_path: str) -> DownloadResult:
        """
        下载文件
        
        Args:
            remote_path: 远程文件路径
            local_path: 本地保存路径
            
        Returns:
            DownloadResult: 下载结果
        """
        try:
            # 获取文件对应的provider
            provider, provider_path = self._resolve_provider_path(remote_path)
            if not provider:
                return DownloadResult(False, message="无法解析文件路径对应的provider")
            
            # 调用provider的下载方法
            return provider.download(provider_path, local_path)
            
        except Exception as e:
            self.logger.error(f"下载文件失败: {e}")
            return DownloadResult(False, message=str(e))
    
    def create_directory(self, path: str) -> bool:
        """
        创建目录
        
        Args:
            path: 目录路径
            
        Returns:
            bool: 创建是否成功
        """
        try:
            provider, provider_path = self._resolve_provider_path(path)
            if not provider:
                self.logger.error("无法解析路径对应的provider")
                return False
            
            return provider.mkdir(provider_path)
            
        except Exception as e:
            self.logger.error(f"创建目录失败: {e}")
            return False
    
    def delete_item(self, path: str) -> bool:
        """
        删除文件或目录
        
        Args:
            path: 文件或目录路径
            
        Returns:
            bool: 删除是否成功
        """
        try:
            provider, provider_path = self._resolve_provider_path(path)
            if not provider:
                self.logger.error("无法解析路径对应的provider")
                return False
            
            return provider.delete(provider_path)
            
        except Exception as e:
            self.logger.error(f"删除失败: {e}")
            return False
    
    def _get_node_by_path(self, path: str) -> Optional[DirectoryNode]:
        """根据路径获取节点"""
        # 先从缓存查找
        if path in self._path_cache:
            return self._path_cache[path]
        
        # 分解路径查找
        if path == "/":
            return self.root
        
        parts = path.strip("/").split("/")
        current_node = self.root
        
        for part in parts:
            if part in current_node.children:
                current_node = current_node.children[part]
            else:
                return None
        
        return current_node
    
    def _get_mount_point(self, path: str) -> str:
        """获取路径对应的挂载点"""
        if path == "/":
            return ""
        
        parts = path.strip("/").split("/")
        return f"/{parts[0]}"
    
    def _resolve_provider_path(self, unified_path: str) -> tuple[Optional[BaseCloudProvider], str]:
        """
        解析统一路径为provider和provider内部路径
        
        Args:
            unified_path: 统一路径
            
        Returns:
            tuple: (provider实例, provider内部路径)
        """
        if unified_path == "/":
            return None, ""
        
        # 获取挂载点
        mount_point = self._get_mount_point(unified_path)
        provider = self.provider_manager.get_provider(mount_point)
        
        if not provider:
            return None, ""
        
        # 计算provider内部路径
        if unified_path == mount_point:
            provider_path = "/"
        else:
            provider_path = unified_path[len(mount_point):]
            if not provider_path.startswith("/"):
                provider_path = "/" + provider_path
        
        return provider, provider_path
    
    def _convert_to_unified_path(self, base_path: str, name: str) -> str:
        """将provider路径转换为统一路径"""
        if base_path == "/":
            return f"/{name}"
        elif base_path.endswith("/"):
            return f"{base_path}{name}"
        else:
            return f"{base_path}/{name}"
    
    def _load_config(self, config_path: str):
        """加载配置文件"""
        # TODO: 实现配置文件加载逻辑
        pass
    
    def list_mount_points(self) -> List[str]:
        """
        列出所有挂载点
        
        Returns:
            List[str]: 挂载点列表
        """
        return self.directory_manager.list_mount_points()
    
    def resolve_path(self, path: str) -> Tuple[str, str]:
        """
        解析路径为挂载点和相对路径
        
        Args:
            path: 要解析的路径
            
        Returns:
            Tuple[str, str]: (挂载点, 相对路径)
        """
        return self.directory_manager.resolve_path(path)
    
    def unmount_provider(self, mount_point: str) -> bool:
        """
        卸载provider
        
        Args:
            mount_point: 挂载点路径
            
        Returns:
            bool: 卸载是否成功
        """
        try:
            # 检查挂载点是否存在
            if mount_point in self.directory_manager.get_mount_points():
                # 移除provider
                provider = self.provider_manager.get_provider(mount_point)
                if provider:
                    provider.logout()
                
                # 移除目录节点
                self.directory_manager.remove_mount_point(mount_point)
                
                self.logger.info(f"成功卸载provider: {mount_point}")
                return True
            else:
                self.logger.warning(f"挂载点不存在: {mount_point}")
                return False
                
        except Exception as e:
            self.logger.error(f"卸载provider失败: {e}")
            return False
    
    def delete_file(self, path: str) -> bool:
        """
        删除文件
        
        Args:
            path: 文件路径
            
        Returns:
            bool: 删除是否成功
        """
        try:
            mount_point, relative_path = self._resolve_provider_path(path)
            if not mount_point:
                return False
            
            provider = self.provider_manager.get_provider(mount_point)
            if not provider:
                return False
            
            return provider.delete(relative_path)
        except Exception as e:
            self.logger.error(f"删除文件失败 {path}: {e}")
            return False
    
    def health_check(self) -> Dict[str, Any]:
        """
        健康检查
        
        Returns:
            Dict[str, Any]: 健康状态信息
        """
        return {
            "status": "healthy",
            "providers": self.provider_manager.get_stats(),
            "mount_points": self.list_mount_points()
        }
    
    def get_provider_stats(self) -> Dict[str, Any]:
        """
        获取provider统计信息
        
        Returns:
            Dict[str, Any]: 统计信息
        """
        return self.provider_manager.get_stats()
    
    def shutdown(self):
        """关闭客户端"""
        self.provider_manager.shutdown()
    
    def __enter__(self):
        """上下文管理器入口"""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器出口"""
        self.shutdown()
    
    def _cleanup_mount_point(self, mount_point: str):
        """清理挂载点相关的节点和缓存"""
        # 从缓存中移除相关路径
        to_remove = [path for path in self._path_cache.keys() 
                    if path.startswith(mount_point)]
        for path in to_remove:
            del self._path_cache[path]
        
        # 从目录树中移除节点
        if mount_point != "/":
            parts = mount_point.strip("/").split("/")
            if len(parts) == 1:
                # 直接子目录
                if parts[0] in self.root.children:
                    del self.root.children[parts[0]]